import main from "./index";
import { ColorToRGB, Gete, Newe, RGBToColor } from "./utils";
import { IColor, IData } from "./models";

export default class {
  Dom: HTMLCanvasElement;
  Width: number;
  Height: number;
  Data = {};
  Transparent = { tr: 0, tg: 0, tb: 0 };
  TestCanvas: HTMLCanvasElement;
  CutRect = [0, 0, 0, 0]; // left, top, width, height
  ShowCut = false;
  private operateCanvas: HTMLCanvasElement;
  private cutStatus = -1;

  constructor() {
    this.Dom = Gete("myCanvas") as HTMLCanvasElement;
    this.TestCanvas = Gete("testCanvas") as HTMLCanvasElement;
    this.operateCanvas = Gete("operateCanvas") as HTMLCanvasElement;
    const { operateCanvas } = this;
    operateCanvas.onmousedown = e => this.mousedown(e);
    operateCanvas.onmousemove = e => this.mousemove(e);
    operateCanvas.onmouseup = () => setTimeout(() => this.mouseup(), 0);
    operateCanvas.onclick = e => this.click(e);
    this.SetCanvasSize();
  }
  
  SetScale(scale: number) {
    const zoom = scale.toString();
    const { Dom, TestCanvas, operateCanvas, Width, Height } = this;
    Dom.style["zoom"] = zoom;
    TestCanvas.style["zoom"] = zoom;
    operateCanvas.width = Width * scale;
    operateCanvas.height = Height * scale;
    this.UpdateCut();
  }
  
  SetCanvasSize(width = 0, height = 0) {
    this.Width = width;
    this.Height = height;
    main.Toolbar.SetTitle();
    const { Dom, operateCanvas, CutRect } = this;
    Dom.width = width;
    Dom.height = height;
    const { Scale } = main.Toolbar;
    operateCanvas.width = width * Scale;
    operateCanvas.height = height * Scale;
    const [left, top, w, h] = CutRect;
    if (left >= width) CutRect[0] = 0;
    if (top >= height) CutRect[1] = 0;
    if (!w || w > width) CutRect[2] = width * Scale;
    if (!h || h > height) CutRect[3] = height * Scale;
    this.UpdateCut();
  }
  
  private mousedown(e: MouseEvent) {
    const { ShowCut, CutRect } = this;
    if (!ShowCut) return;
    
    if (main.Marker.Data.length) return; // 有标注时不可拖拽剪切点
    
    const { Scale } = main.Toolbar;
    const mx = Math.round(e.offsetX / Scale);
    const my = Math.round(e.offsetY / Scale);
    
    // 确定点到了哪个剪切点
    const dotSize = 8, dotHalf = dotSize/2;
    const [left, top, width, height] = CutRect;
    const right = left + width, bottom = top + height;
    const center = left + width/2, middle = top + height/2;
    let cutStatus = -1;
    if (mx >= center - dotHalf && mx < center + dotHalf) {
      if (my >= top && my < top + dotSize) cutStatus = 0;
      else if (my >= bottom - dotSize && my < bottom) cutStatus = 6;
    } else if (my >= middle - dotHalf && my < middle + dotHalf) {
      if (mx >= left && mx < left + dotSize) cutStatus = 9;
      else if (mx >= right - dotSize && mx < right) cutStatus = 3;
    } else if (my >= top && my < top + dotSize) {
      if (mx >= left && mx < left + dotSize) cutStatus = 11;
      else if (mx >= right - dotSize && mx < right) cutStatus = 1;
    } else if (my >= bottom - dotSize && my < bottom) {
      if (mx >= left && mx < left + dotSize) cutStatus = 7;
      else if (mx >= right - dotSize && mx < right) cutStatus = 5;
    }
    this.cutStatus = cutStatus;
  }
  
  private mousemove(e: MouseEvent) {
    const { ShowCut, cutStatus, CutRect } = this;
    if (!ShowCut) return;
    
    if (main.Marker.Data.length) return;
    
    const { Scale } = main.Toolbar;
    const mx = Math.round(e.offsetX / Scale);
    const my = Math.round(e.offsetY / Scale);
    if (e.shiftKey) console.log(mx, my);
    
    const dotSize = 8, dotHalf = dotSize/2;
    const [left, top, width, height] = CutRect;
    const right = left + width, bottom = top + height;
    const center = left + width/2, middle = top + height/2;
    
    if (cutStatus < 0) {
      // 鼠标箭头指向不同的剪切点时鼠标箭头外观会相应的变化。
      let cursor = "default";
      if (mx >= center - dotHalf && mx < center + dotHalf) {
        if (my >= top && my < top + dotSize) cursor = "n-resize"; // 0
        else if (my >= bottom - dotSize && my < bottom) cursor = "n-resize"; // 6
      } else if (my >= middle - dotHalf && my < middle + dotHalf) {
        if (mx >= left && mx < left + dotSize) cursor = "e-resize"; // 9
        else if (mx >= right - dotSize && mx < right) cursor = "e-resize"; // 3
      } else if (my >= top && my < top + dotSize) {
        if (mx >= left && mx < left + dotSize) cursor = "nw-resize"; // 11
        else if (mx >= right - dotSize && mx < right) cursor = "ne-resize"; // 1
      } else if (my >= bottom - dotSize && my < bottom) {
        if (mx >= left && mx < left + dotSize) cursor = "ne-resize"; // 7
        else if (mx >= right - dotSize && mx < right) cursor = "nw-resize"; // 5
      }
      this.operateCanvas.style.cursor = cursor;
      return;
    }
    
    // 处理鼠标拖拽剪切点
    const rect = [...CutRect];
    if ([0, 1, 11].indexOf(cutStatus) >= 0) {
      rect[1] = my;
      rect[3] = height - (my - top);
      if (cutStatus === 11) {
        rect[0] = mx;
        rect[2] = width - (mx - left);
      } else if (cutStatus === 1) rect[2] = mx - left;
    } else if (cutStatus === 3) rect[2] = mx - left;
    else if (cutStatus === 9) {
      rect[0] = mx;
      rect[2] = width - (mx - left);
    } else if ([5, 6, 7].indexOf(cutStatus) >= 0) {
      rect[3] = my - top;
      if (cutStatus === 5) rect[2] = mx - left;
      else if (cutStatus === 7) {
        rect[0] = mx;
        rect[2] = width - (mx - left);
      }
    }
    const min = 24;
    if ([3, 5, 6].indexOf(cutStatus) >= 0) {
      if (rect[2] < min) rect[2] = min;
      if (rect[3] < min) rect[3] = min;
    }
    if (rect[2] < min || rect[3] < min) return;
    
    this.CutRect = rect;
    this.UpdateCut();
  }
  
  private mouseup() {
    const { ShowCut, cutStatus, CutRect, Data } = this;
    if (!ShowCut) return;
    
    if (main.Marker.Data.length) return;
    
    if (cutStatus < 0) return;
    
    this.cutStatus = -1;
    
    // 拖拽剪切点后重新设置调色板
    const [l, t, w, h] = CutRect;
    const r = l + w, b = t + h;
    for (const filename in Data) {
      const { colorData, palette } = Data[filename] as IData;
      palette.splice(0);
      for (let y = t; y < b; y += 1) {
        for (let x = l; x < r; x += 1) {
          const color = colorData[y][x];
          if (this.IsTransparent(color, 0)) continue;
          
          const rgb = ColorToRGB(color);
          if (palette.indexOf(rgb) === -1) palette.push(rgb);
        }
      }
    }
    
    main.Feedback.Update();
    main.FileList.Update();
  }
  
  private click(e: MouseEvent) {
    if (this.cutStatus >= 0) return; // 拖拽剪切点期间禁止新增标注点
    
    const { Scale } = main.Toolbar;
    const mx = Math.round(e.offsetX / Scale);
    const my = Math.round(e.offsetY / Scale);
    
    const { ShowCut, CutRect, Width, Height, Data } = this;
    if (ShowCut) {
      // 限制点击区域
      const [left, top, width, height] = CutRect;
      const right = left + width, bottom = top + height;
      if (mx < left || mx > right || my < top || my > bottom) return;
    }
    
    // 防止数组索引越界
    if (mx >= Width || my >= Height) return;
    
    const { colorData } = Data[main.FileList.FileName] as IData;
    const color = colorData[my][mx];
    if (this.IsTransparent(color)) return; // 略过透明色
    
    main.Marker.Click(mx, my);
  }
  
  ImageLoad(img: HTMLImageElement, filename: string) {
    const { width, height } = img;
    const { Width, Height } = this;
    if (!this.Data[filename])
      this.Data[filename] = { colorData: [], palette: [] } as IData;
    if (width !== Width || height !== Height) {
      // 若新打开的图片的宽高与之前的不同，则删除之前的。
      delete this.Data[main.FileList.FileName];
      main.Marker.Clear();
      main.ObjList.Clear();
    }
    
    this.SetCanvasSize(width, height);
    let ct = this.Dom.getContext("2d");
    ct.drawImage(img, 0, 0);
    
    const imageData = ct.getImageData(0, 0, width, height);
    const { data } = imageData;
    
    // 获取透明色，基于左下角
    const lb = width * (height - 1) * 4;
    this.Transparent = { tr: data[lb], tg: data[lb + 1], tb: data[lb + 2] };
    
    // 抠图，准备调色板
    const data2: IColor[][] = [];
    let rowData: IColor[] = [], x = 0, y = 0;
    // 之前的，要沿用之前的像素占用情况。
    const { colorData } = this.Data[main.FileList.FileName || filename] as IData;
    const { palette } = this.Data[filename] as IData;
    palette.splice(0);
    const { ShowCut, CutRect } = this;
    let [left, top, w, h] = CutRect;
    if (!ShowCut) {
      left = 0;
      top = 0;
      w = width;
      h = height;
    }
    const right = left + w, bottom = top + h;
    
    for (let i = 0; i < data.length; i += 4) {
      const r = data[i], g = data[i + 1], b = data[i + 2];
      const a = colorData.length ? colorData[y][x].a : data[i + 3];
      const color: IColor = { r, g, b, a };
      
      if (this.IsTransparent(color, 0)) data[i + 3] = 0; // 设置成透明
      else if (x >= left && x < right && y >= top && y < bottom) {
        const rgb = ColorToRGB(color);
        if (palette.indexOf(rgb) === -1) palette.push(rgb);
      }
      
      rowData.push(color);
      x++;
      if (x >= width) {
        data2.push(rowData);
        rowData = [];
        x = 0;
        y++;
        if (y >= height) this.Data[filename].colorData = data2;
      }
    }
    
    const canvas = Newe({
      type: "canvas",
      para: { width, height },
    }) as HTMLCanvasElement;
    ct = canvas.getContext("2d");
    ct.putImageData(imageData, 0, 0);
    this.Data[filename].canvas = canvas;
  }
  
  // 统计每张原图的颜色数量，若不一致，则提醒用户注意。
  TotalColor() {
    const { Data } = this;
    let size = 0, isSame = true;
    // const sizeData: string[] = [];
    
    for (const filename in Data) {
      const { palette } = Data[filename] as IData;
      if (!size) size = palette.length;
      else isSame = Math.abs(size - palette.length) < 8; // 一定的兼容区间
      // sizeData.push(filename + ":" + palette.length);
    }
    
    if (isSame) return;
    
    // const msg = "警告：多张图片的颜色种数不统一，在使用时可能会有色差。\n" +
    //   sizeData.toString();
    // alert(msg);
  }
  
  Update() {
    const { Dom, Data } = this;
    const { FileName } = main.FileList;
    const data = Data[FileName];
    if (!data) return;
    
    Dom.width--;
    Dom.width++;
    const ct = Dom.getContext("2d");
    const { canvas } = data as IData;
    ct.drawImage(canvas, 0, 0);
  }
  
  IsTransparent(color: IColor, alpha = 0xfe) {
    if (!color) return true;
    if (color.a <= alpha) return true;
    
    const { r, g, b } = color;
    const { tr, tg, tb } = this.Transparent;
    return r === tr && g === tg && b === tb;
  }
  
  SwitchMode() {
    const { IsTest } = main.Toolbar;
    const canvasWrap = Gete("canvasWrap");
    const testWrap = Gete("testWrap");
    canvasWrap.hidden = IsTest;
    testWrap.style.display = IsTest ? "flex" : "none";
  }
  
  SetTestCanvas(bmpData: string[][] = [], rgbIndexData: number[] = []) {
    this.TestCanvas.hidden = false;
    if (bmpData.length) {
      this.TestCanvas.height = bmpData[0].length;
      this.TestCanvas.width = bmpData[0][0].length;
      this.TestCanvas["bmpData"] = bmpData;
    }
    if (rgbIndexData.length) this.TestCanvas["rgbIndexData"] = rgbIndexData;
    
    const { palette } = this.Data[main.FileList.FileName] as IData;
    let colorData: IColor[] = [];
    if (this.TestCanvas["rgbIndexData"])
      colorData = this.TestCanvas["rgbIndexData"].map((rgbIndex: number) => {
        const rgb = palette[rgbIndex];
        return RGBToColor(rgb);
      });
    
    // 组合图层
    let bmpData2: [][][] = [];
    if (this.TestCanvas["bmpData"])
      bmpData2 = this.TestCanvas["bmpData"].map((layer: string[], index: number) => {
          return layer.map(row => {
            const rowData = row.split("");
              return rowData.map(dot => dot == "0" ? 0 : colorData[index]);
        });
      });
    
    const { width, height } = this.TestCanvas;
    const imageData = new ImageData(width, height);
    const { data } = imageData;
    bmpData2.map(layer => {
      layer.map((rowData, y) => {
        rowData.map((color: IColor, x) => {
          if (!color) return;
          
          const { r, g, b } = color;
          const i = (y * width + x) * 4;
          data[i] = r;
          data[i + 1] = g;
          data[i + 2] = b;
          data[i + 3] = 0xff;
        });
      });
    });
    
    const ct = this.TestCanvas.getContext("2d");
    ct.putImageData(imageData, 0, 0);
  }
  
  SwitchCut() {
    const { ShowCut, operateCanvas } = this;
    const { width } = operateCanvas;
    if (!width) return;
    
    this.ShowCut = !ShowCut;
    this.UpdateCut();
    
    main.Toolbar.PickBtn.disabled = ShowCut;
  }
  
  UpdateCut() {
    const { ShowCut, operateCanvas, CutRect } = this;
    if (!operateCanvas.width) return;
    
    operateCanvas.width--;
    operateCanvas.width++;
    
    if (!ShowCut) return;
    
    const { Scale } = main.Toolbar;
    const dotSize = 8, dotHalf = dotSize/2;
    let [left, top, width, height] = CutRect;
    left *= Scale;
    top *= Scale;
    width *= Scale;
    height *= Scale;
    const right = left + width, bottom = top + height;
    const center = left + width/2, middle = top + height/2;
    
    let { Width, Height } = this;
    Width *= Scale;
    Height *= Scale;
    const ct = operateCanvas.getContext("2d");
    ct.fillStyle = "#00000080";
    ct.fillRect(0, 0, Width, top);
    ct.fillRect(0, bottom, Width, Height - bottom);
    ct.fillRect(0, top, left, height);
    ct.fillRect(right, top, Width - right, height);
    ct.strokeStyle = "#0050b3";
    ct.strokeRect(left, top, width, height);
    
    if (main.Marker.Data.length) return; // 有标注时不可拖拽剪切点
    
    ct.fillStyle = "#91d5ff";
    ct.fillRect(left, top, dotSize, dotSize); // 11
    ct.fillRect(center - dotHalf, top, dotSize, dotSize); // 12
    ct.fillRect(right - dotSize, top, dotSize, dotSize); // 1
    ct.fillRect(right - dotSize, middle - dotHalf, dotSize, dotSize); // 3
    ct.fillRect(right - dotSize, bottom - dotSize, dotSize, dotSize); // 5
    ct.fillRect(center - dotHalf, bottom - dotSize, dotSize, dotSize); // 6
    ct.fillRect(left, bottom - dotSize, dotSize, dotSize); // 7
    ct.fillRect(left, middle - dotHalf, dotSize, dotSize); // 9
  }
  
  GetRect(mx: number, my: number) {
    const { Data, Width, Height, ShowCut, CutRect } = this;
    const { colorData } = Data[main.FileList.FileName] as IData;
    let l = 0, t = 0, w = Width, h = Height;
    if (ShowCut) {
      l = CutRect[0];
      t = CutRect[1];
      w = CutRect[2];
      h = CutRect[3];
    }
    const r = l + w, b = t + h;
    
    // 确定标注处精灵的轮廓(边框)，从标注位置向四周逐点判断
    let y0 = t; // 向上判断，直到遇到透明像素
    for (let y = my; y >= t; y -= 1)
      if (this.IsTransparent(colorData[y][mx])) {
        y0 = y + 1;
        break;
      }
    let y1 = b - 1; // 向下判断，直到遇到透明像素
    for (let y = my; y < b; y += 1)
      if (this.IsTransparent(colorData[y][mx])) {
        y1 = y - 1;
        break;
      }
    let x0 = l; // 向左判断，直到遇到透明像素
    for (let x = mx; x >= l; x -= 1)
      if (this.IsTransparent(colorData[my][x])) {
        x0 = x + 1;
        break;
      }
    let x1 = r - 1; // 向右判断，直到遇到透明像素
    for (let x = mx; x < r; x += 1)
      if (this.IsTransparent(colorData[my][x])) {
        x1 = x - 1;
        break;
      }
    let top = t; // 向上判断，若一整行都是透明像素，则确定此行是上边界
    for (let y = y0; y >= t; y -= 1) {
      let isAllTransparent = true;
      for (let x = x0; x <= x1; x += 1) {
        if (!this.IsTransparent(colorData[y][x])) {
          isAllTransparent = false;
          break;
        }
      }
      if (isAllTransparent) {
        top = y + 1;
        break;
      }
    }
    let bottom = b; // 向下判断，若一整行都是透明像素，则确定此行是下边界
    for (let y = y1; y < b; y += 1) {
      let isAllTransparent = true;
      for (let x = x0; x <= x1; x += 1) {
        if (!this.IsTransparent(colorData[y][x])) {
          isAllTransparent = false;
          break;
        }
      }
      if (isAllTransparent) {
        bottom = y;
        break;
      }
    }
    let left = l; // 向左判断，若一整列都是透明像素，则确定此列是左边界
    for (let x = x0; x >= l; x -= 1) {
      let isAllTransparent = true;
      for (let y = top; y < bottom; y += 1) {
        if (!this.IsTransparent(colorData[y][x])) {
          isAllTransparent = false;
          break;
        }
      }
      if (isAllTransparent) {
        left = x + 1;
        break;
      }
    }
    let right = r; // 向右判断，若一整列都是透明像素，则确定此列是右边界
    for (let x = x1; x < r; x += 1) {
      let isAllTransparent = true;
      for (let y = top; y < bottom; y += 1) {
        if (!this.IsTransparent(colorData[y][x])) {
          isAllTransparent = false;
          break;
        }
      }
      if (isAllTransparent) {
        right = x;
        break;
      }
    }
    
    const width = right - left, height = bottom - top;
    return { left, top, width, height };
  }
  
  SetRect(markerIndex: number, occupy = true) {
    const data = this.Data[main.FileList.FileName];
    if (!data) return;
    
    const markerItem = main.Marker.Data[markerIndex];
    const { width, height, sprite } = markerItem;
    const { left, top } = sprite;
    const right = left + width, bottom = top + height;
    const value = occupy ? 0xfe : 0xff;
    const { colorData } = data as IData;
    for (let i = top; i < bottom; i += 1)
      for (let j = left; j < right; j += 1)
        if (colorData[i][j].a && colorData[i][j].a !== value)
          colorData[i][j].a = value;
  }
};
